﻿package com.zouloux.core.navigation 
{
	import com.zouloux.core.data.IDataItem;
	import com.zouloux.utils.ArrayUtils;
	import com.zouloux.utils.StringUtils;
	/**
	 * ...
	 * @author ZoulouX
	 */
	public class RouteItem implements IDataItem
	{
		
		/**
		 * Les délimiteurs pour parser les URL
		 */
		public static const LEFT_DELIMITER			:String = "{";
		public static const RIGHT_DELIMITER			:String = "}";
		public static const RULE_DELIMITER			:String = ":";
		
		/**
		 * Les regex pour les caractères
		 */
		public static const NUM_PATTERN				:String = "([0-9]*)";
		public static const ALPHA_PATTERN			:String = "([a-zA-Z]*)";
		public static const ALPHANUM_PATTERN		:String = "([a-zA-Z0-9-_]*)";
		
		/**
		 * L'url codée de cette route
		 */
		protected var _url				:String 		= "";
		
		/**
		 * La vue associée
		 */
		protected var _view				:String 		= "";
		
		/**
		 * L'action associée (par défaut, index)
		 */
		protected var _action			:String 		= "index";
		
		/**
		 * Les variables
		 */
		protected var _vars				:Object  		= {};
		
		/**
		 * La liste des variables attendues
		 */
		protected var _variableNames	:Array			= [];
		
		/**
		 * Une instance du bootstrap
		 */
		protected var _bootstrap		:Bootstrap;
		
		/**
		 * Le pattern pour checker les URL
		 */
		protected var _pattern			:String;
		
		/**
		 * La vue
		 */
		public function get view():String { return _view; }
		public function set view(value:String):void 
		{
			_view = value;
		}
		
		/**
		 * L'action
		 */
		public function get action():String { return _action; }
		public function set action(value:String):void 
		{
			_action = value;
		}
		
		/**
		 * Les variables
		 */
		public function get vars():Object { return _vars; }
		public function set vars(value:Object):void 
		{
			_vars = value;
		}
		
		
		/**
		 * RouteItem, permet de définir une route de navigation à double sens
		 */
		public function RouteItem (pBootstrapInstance:Bootstrap, pXmlNode:XML)
		{
			// Vérifier qu'il y ai une vue associée à une URL
			if (pXmlNode.@url.toString() != "" && pXmlNode.@view.toString() != "")
			{
				// Enregistrer les deux
				_url = pXmlNode.@url.toString();
				_view = pXmlNode.@view.toString();
				
				// Enregistrer l'action s'il y en a une
				if (pXmlNode.@action.toString() != "")
					_action = pXmlNode.@action.toString();
				
				// Parser la route
				parseRoute();
			}
			else throw new Error("Unvalid RouteItem (must have @url and @view");
		}
		
		/**
		 * Parser la route
		 */
		protected function parseRoute ():void
		{
			// Dupliquer l'url pour le traitement
			var url			:String 	= _url;
			
			// Le pattern
			_pattern = "";
			
			// L'index de la variable
			var varIndex:uint = 0;
			
			// Tant qu'il y a des variables dans l'URL
			while (url.indexOf(LEFT_DELIMITER) != -1 && url.indexOf(RIGHT_DELIMITER) != -1)
			{
				// Récupérer le début la fin du nom de la variable
				var index1:int = url.indexOf(LEFT_DELIMITER);
				var index2:int = url.indexOf(RIGHT_DELIMITER);
				
				// Récupérer son nom
				var varName:String = url.slice(index1 + 1, index2);
				
				// La règle
				var rule:String;
				
				// Vérifier si on a une séparateur
				if (varName.indexOf(RULE_DELIMITER) != -1)
				{
					// Séparer avant et après le séparateur
					var splited:Array = varName.split(RULE_DELIMITER, 2);
					
					// Récupérer le varName
					varName = splited[0];
					
					// Récupérer le nom de la règle
					var ruleName:String = splited[1];
					
					// Récupérer la règle selon son nom
					if (ruleName == "alpha")
						rule = ALPHA_PATTERN;
					else if (ruleName == "alphanum")
						rule = ALPHANUM_PATTERN;
					else if (ruleName == "num")
						rule = NUM_PATTERN;
					else
						rule = ruleName;
				}
				else
					rule = ALPHANUM_PATTERN;
				
				// Enregistrer le nom de cette variables
				_variableNames[varIndex] = varName;
				
				// Ajouter le premier bout ainsi que l'url
				_pattern += StringUtils.cleanRule(url.slice(0, index1)) + rule;
				
				// Retailler l'url après le paramètre que l'on vient de parser
				url = url.slice(index2 + 1, url.length);
				
				// Ajouter la fin de l'url
				if (url.indexOf(RIGHT_DELIMITER) == -1)
					_pattern += StringUtils.cleanRule(url);
				
				// Incrémenter l'index
				varIndex ++;
			}
			
			// Vérifier si on est passé dedans
			if (_pattern == "")
				_pattern = _url;
		}
		
		/**
		 * Vérifier si une URL correspond à ce pattern
		 * @param	pUrl : L'url en question
		 * @return : true si cette route correspond au pattern
		 */
		public function checkRoute (pUrl:String):Boolean
		{
			// Récupérer la regex
			var regex:RegExp = new RegExp("^" + _pattern + "$");
			
			// Vérifier
			if (regex.test(pUrl))
			{
				// Enregistrer les variables
				var search:Object = regex.exec(pUrl);
				
				// Parser le résultat de recherche
				for (var i:* in search)
				{
					// Coller la variable dans les params
					_vars[_variableNames[i - 1]] = search[i];
				}
				
				// C'est ok
				return true;
			} else return false;
			
			// Vérifier si l'url est valide
			return ;
		}
		
		/**
		 * Vérifier si un routage inverse est faisable
		 * @param	pView : Le nom de la vue
		 * @param	pAction : Le nom de l'action
		 * @param	pVars : La liste des variables
		 * @return : Un boolean
		 */
		public function checkReverse (pView:String, pAction:String = "index", pVars:Object = null):Boolean
		{
			// Initialiser le tableau null
			if (pVars == null)
				pVars = {};
			
			// Vérifier le nom de la vue et de l'action
			if (pView == _view && pAction == _action)
			{
				// Si on a toutes les variables
				var allVariablesOk:Boolean = true;
				
				// Vérifier le nom de chaque variable
				for (var i:* in pVars)
				{
					if (!ArrayUtils.inArray(_variableNames, i))
					{
						allVariablesOk = false;
						break;
					}
				}
				
				// On retourne l'état
				return allVariablesOk;
			}
			else return false;
		}
		
		/**
		 * Créer l'URL correspondante à cette URL avec ces paramètres
		 * @param	pView : La vue ciblée
		 * @param	pActions : L'action ciblée
		 * @param	pVars : Les variables ciblées
		 * @return : L'url convertie
		 */
		public function reverseURL (pVars:Object = null):String
		{
			// Dupliquer l'url pour le traitement
			var url			:String 	= _url;
			
			// La nouvelle URL pour la sortie
			var newURL		:String 	= "";
			
			// Tant qu'il y a des variables dans l'URL
			while (url.indexOf(LEFT_DELIMITER) != -1 && url.indexOf(RIGHT_DELIMITER) != -1)
			{
				// Récupérer le début la fin du nom de la variable
				var index1:int = url.indexOf(LEFT_DELIMITER);
				var index2:int = url.indexOf(RIGHT_DELIMITER);
				
				// Récupérer son nom
				var varName:String = url.slice(index1 + 1, index2);
				
				// Découper les :
				if (varName.indexOf(RULE_DELIMITER) != -1)
					varName = varName.slice(0, varName.indexOf(RULE_DELIMITER));
				
				// Ajouter le premier bout ainsi que l'url
				newURL += url.slice(0, index1) + StringUtils.slugify(pVars[varName]);
				
				// Retailler l'url après le paramètre que l'on vient de parser
				url = url.slice(index2 + 1, url.length);
				
				// Ajouter la fin de l'url
				if (url.indexOf(RIGHT_DELIMITER) == -1)
					newURL += url;
			}
			
			// Retourner la nouvelle URL
			return newURL == "" ? _url : newURL;
		}
	}
}